#include <seccomp.h>
#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include "../comm/log.hpp"

namespace ns_sandbox
{
    using namespace ns_log;
    void Die(const std::string &msg)
    {
        LOG(LogLevel::ERROR) << msg;
        raise(SIGKILL);
    }
    //    static void SetProcessLimit(int cpu_limit,int mem_limit)
    //    {
    //        struct rlimit cpur;
    //        cpur.rlim_cur = cpu_limit;
    //        cpur.rlim_max = cpur.rlim_cur + 1;//降低硬上限,避免用户的非法行为:不具备相应权限的进程,只能降低,不能再提升:使用1s进行缓冲
    //        // cpur.rlim_max = RLIM_INFINITY;
    //        setrlimit(RLIMIT_CPU,&cpur);

    //        struct rlimit memr;
    //        memr.rlim_cur = mem_limit * 1024;
    //        memr.rlim_max = memr.rlim_cur + 1;//1byte用于缓冲
    //        // memr.rlim_max = RLIM_INFINITY;
    //        setrlimit(RLIMIT_AS,&memr);
    //    }

    void SetProcessLimit(int cpu_limit, int mem_limit)
    {
        // 限制:CPU,AS,CORE,FILE_SIZE
        struct rlimit rs;

        getrlimit(RLIMIT_CPU, &rs);
        rs.rlim_cur = 1; // s为单位,软硬限制之间需要有缓冲空间,否则SIGXCPU升级为SIGKILL
        setrlimit(RLIMIT_CPU, &rs);

        getrlimit(RLIMIT_AS, &rs);
        rs.rlim_cur = mem_limit << 10; // 传入的mem_limit以kb为单位,扩展为MB
        setrlimit(RLIMIT_AS, &rs);

        getrlimit(RLIMIT_CORE, &rs);
        rs.rlim_cur = 0; // 禁止core_dump行为
        setrlimit(RLIMIT_CORE, &rs);

        getrlimit(RLIMIT_FSIZE, &rs);
        rs.rlim_cur = 1024 << 20; // 默认规定文件大小不超过1MB
        setrlimit(RLIMIT_FSIZE, &rs);
    }

    // 这种filter写法导致自身无法进程替换
    //  bool Filter()
    //  {
    //      //通过seccomp过滤相关系统调用

    //     //1.初始化seccmp,默认杀死所有系统调用
    //     scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
    //     if(!ctx)
    //         return Die("seccomp_init");
    //     //2.白名单:允许程序正常运行的必备系统调用,规范用户行为
    //     // int allow[] = {SCMP_SYS(read),SCMP_SYS(write),SCMP_SYS(exit_group),SCMP_SYS(exit)};
    //     int allow[] = {
    //     /* 生存必备 */
    //     SCMP_SYS(exit), SCMP_SYS(exit_group),
    //     SCMP_SYS(brk),                      // 堆
    //     SCMP_SYS(mmap), SCMP_SYS(munmap),
    //     SCMP_SYS(mprotect),                 // TLS / RELRO
    //     SCMP_SYS(arch_prctl),               // 设 FS 段
    //     SCMP_SYS(set_tid_address),          // 线程清理
    //     SCMP_SYS(access),
    //     SCMP_SYS(openat),
    //     SCMP_SYS(fstat),                    //查看文件的元信息
    //     SCMP_SYS(mprotect),

    //     /* 只做 IO */
    //     SCMP_SYS(pread64),
    //     SCMP_SYS(read),                     // fd 0
    //     SCMP_SYS(write),                    // fd 1,2

    //     /* 静态链接也会调一次，取页大小 */
    //     SCMP_SYS(sysinfo)                   // 仅 _start 里一次，可换 SCMP_SYS(clock_gettime)
    //     };
    //     for(int sys : allow)
    //         if(seccomp_rule_add(ctx,SCMP_ACT_ALLOW,sys,0) < 0)
    //              return Die("seccomp_rule_add");
    //     //3.限制read/write的行为
    //     seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 1,
    //              SCMP_A0(SCMP_CMP_EQ, 0));   //只有0可以
    //     seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1,
    //              SCMP_A0(SCMP_CMP_LE, 2));   // 0,1,2 都放行，但 0 实际不会写
    //     //4.用户到内核
    //     if(seccomp_load(ctx) < 0)
    //         return Die("seccomp_load");
    //     //5.成功写到内核后,释放空间
    //     seccomp_release(ctx);
    //     return true;
    // }

    int run_tracer(const std::string &file_exe, int mem_limit, int cpu_limit, int infd, int outfd, int errfd, int &legal)
    {
        pid_t child_pid = fork();
        if (child_pid < 0)
            Die("fork failed");
        if (child_pid == 0)
        {
            // 第一部分：过滤设置部分
            // 1. 设置一个 seccomp 过滤器,本质都是seccomp事件
            scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW); // 默认允许所有
            if (!ctx)
                Die("seccomp_init failed");

            // 2. 黑名单策略 - 追踪 execve

            const int blacklist_syscalls_arr[] = {
                // 1. 权限提升与系统控制类
                SCMP_SYS(setuid),     // 更改用户 ID
                SCMP_SYS(setgid),     // 更改组 ID
                SCMP_SYS(setreuid),   // 更改实际/有效用户 ID
                SCMP_SYS(setresuid),  // 更改实际/有效/保存的用户 ID
                SCMP_SYS(ptrace),     // 进程跟踪与调试
                SCMP_SYS(setrlimit),  // 设置系统资源限制
                SCMP_SYS(getrlimit),  // 获取系统资源限制


                // 2. 网络与通信类
                SCMP_SYS(socket),     // 创建套接字 (网络连接)
                SCMP_SYS(connect),    // 连接到远程主机
                SCMP_SYS(bind),       // 绑定本地地址/端口
                SCMP_SYS(listen),     // 监听传入连接
                SCMP_SYS(sendto),     // 发送网络数据
                SCMP_SYS(recvfrom),   // 接收网络数据

                // 3. 进程创建与管理类
                SCMP_SYS(fork),     // 创建子进程 (防止 Fork Bomb)
                SCMP_SYS(vfork),    // 创建子进程 (加速版)
                SCMP_SYS(clone),    // 创建线程或进程
                SCMP_SYS(execve),   // 执行新程序
                SCMP_SYS(kill),     // 发送信号给进程
                SCMP_SYS(nice),     // 更改进程调度优先级

                // 4. 文件系统修改类
                SCMP_SYS(unlink),   // 删除文件
                SCMP_SYS(unlinkat), // 删除文件（基于目录文件描述符）
                SCMP_SYS(rmdir),    // 删除目录
                SCMP_SYS(rename),   // 重命名文件
                SCMP_SYS(chown),    // 更改文件所有者
                SCMP_SYS(chmod),    // 更改文件权限
                SCMP_SYS(mknod),    // 创建特殊文件（如设备文件）
                SCMP_SYS(mkdir),    // 创建目录
            };
            
            int cnt = 0;
            for(auto& e : blacklist_syscalls_arr)
            {
                if(seccomp_rule_add(ctx, SCMP_ACT_TRACE(cnt++),e, 0) < 0)
                    Die("seccomp_rule_add execve failed");
            }

            //当然其它一些无需判断的系统调用直接使用SCMP_ACT_KILL即可
            //常用选项:SCMP_ACT_ALLOW,SCMP_ACT_KILL,SCMP_ACT_TRACE,SCMP_ACT_TRAP,SCMP_ACT_ERRNO(errno)等等

            // 3. 允许父进程(Tracer)来追踪自己:非常重要,没有这个,整个ptrace逻辑都无效
            // 可以子进程主动允许父进程TRACEME,也可以父进程主动追踪子进程
            ptrace(PTRACE_TRACEME, 0, NULL, NULL);
           
            // 4. 重定向标准输入输出
            dup2(infd, 0);
            dup2(outfd, 1);
            dup2(errfd, 2);
            close(infd);//占了两个位置,需要释放一个
            close(outfd);
            close(errfd);
            SetProcessLimit(cpu_limit, mem_limit);

            // 5. 发送信号告诉父进程我准备好了
            raise(SIGSTOP); 

            // 6. 将过滤器从用户空间写入到内核中
            if (seccomp_load(ctx) < 0)
                Die("seccomp_load failed");
            // 7.释放ctx
            seccomp_release(ctx);
            
            char *file_path = (char *)file_exe.c_str();
            char *const argv[] = {file_path, nullptr};
            execve(file_exe.c_str(), argv, environ);
            // 进程替换失败
            exit(1);
        }

        // 第二部分:追踪部分
        // --- 父进程 (Tracer) ---
        int status = 0;
        bool first_execve_done = false;

        // 等待子进程的第一次暂停
        waitpid(child_pid, &status, 0);
        if ((status & 0x7f) == SIGKILL)
            return status;
            
        // 设置 PTRACE 选项，使得父进程能够真正追踪子进程的seccomp事件
        if (ptrace(PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACESECCOMP) < 0)
            Die("ptrace failed");

        // 让子进程继续运行
        ptrace(PTRACE_CONT, child_pid, NULL, NULL);
        // 进入主监控循环
        while (true)
        {
            waitpid(child_pid, &status, 0);

            // 如果子进程正常或因信号退出，则跳出循环
            if (WIFEXITED(status) || WIFSIGNALED(status))
            {
                return status;
            }

            int event = (status >> 16) & 0xff;

            // 检查是否是 seccomp 事件
            if (event == PTRACE_EVENT_SECCOMP)
            {
                // 获取触发事件的系统调用号
                struct user_regs_struct regs;
                ptrace(PTRACE_GETREGS, child_pid, NULL, &regs);
                unsigned long syscall_num = regs.orig_rax; // x86_64

                if (syscall_num == __NR_execve)
                {
                    if (!first_execve_done)
                    {
                        // 这是第一次，是合法的！允许它。
                        first_execve_done = true;
                    }
                    else
                    {
                        // 这是后续的 execve，非法！杀死子进程
                        //seccomp事件中,不属于信号劫持的逻辑,所以无法使用ptrace进行信号的重新注入
                        //ptrace注入的信号不会再被ptrace拦截
                        //SIGKILL信号不受任何PTRACE影响
                        ptrace(PTRACE_KILL, child_pid, NULL, NULL);//这是属于再次发送信号,不属于信号的重新注入逻辑
                        legal = 0;//非法系统调用
                        break;
                    }
                }
                else
                {
                    //其他被 seccomp 追踪的系统调用
                    ptrace(PTRACE_KILL, child_pid, NULL, NULL);//这里也是一样的
                    legal = 0;//非法系统调用
                    break;
                }
            }
            // 确认是接收到信号停止的:ptrace捕捉其它信号的
            else if (WIFSTOPPED(status))
            {
                int sig = WSTOPSIG(status);
                //非SIGTRAP情况:这种情况是被捕捉为SECCOMP事件的系统调用结束时的发送信号的行为,应该被忽略
                //PTRACE_CONT:继续运行,无其他限制
                //PTRACE_SYSCALL:则是在每个系统调用开始和结束时,都会SIGTRAP
                if (sig != SIGTRAP)
                {
                    ptrace(PTRACE_CONT, child_pid, NULL, sig);
                    continue;//当然这里也可以continue,break就是走最后的waitpid逻辑
                }
            }
            // 让子进程继续运行
            ptrace(PTRACE_CONT, child_pid, NULL, NULL);
        }
        waitpid(child_pid, &status, 0);
        return status;
    }

}
